﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;


namespace IQIGame.Onigao.Framework
{
    internal static class ClassPoolCheckUtil
    {
        private static Dictionary<Type, HashSet<FieldInfo>> _typeFieldInfoCache = new Dictionary<Type, HashSet<FieldInfo>>();
        private static Dictionary<Type, object> _typeTempleteObjCache = new Dictionary<Type, object>();
        private static HashSet<FieldInfo> _resetCheckedFields = new HashSet<FieldInfo>();

        public static void CheckPoolClassReset(Type type, object data)
        {
            try
            {
                //float startTime = UnityEngine.Time.realtimeSinceStartup;
                Dictionary<FieldInfo, string> errorFieldInfos = new Dictionary<FieldInfo, string>();
                GetErrorField(data, GetTypeDefaultInstance(type), "", errorFieldInfos, new HashSet<object>());
                if (errorFieldInfos.Count > 0)
                {
                    foreach (var kv in errorFieldInfos)
                    {
                        string checkFieldInfo = kv.Value;
                        checkFieldInfo = checkFieldInfo.Replace(">k__BackingField", "");
                        checkFieldInfo = checkFieldInfo.Replace("<", "");

                        if (!_resetCheckedFields.Contains(kv.Key))
                        {
                            _resetCheckedFields.Add(kv.Key);
                            UnityEngine.Debug.LogError($"ClassPool Get Error: Data is Not Reset Complete type = {type}, field = {checkFieldInfo}");
                        }
                    }

                }
                //UnityEngine.Debug.LogError("check cost " + (UnityEngine.Time.realtimeSinceStartup - startTime));
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogError($"ClassPool Get Error: Data is exception type = {type.Name}");
                UnityEngine.Debug.LogError(e);
            }
        }

        private static void GetAllFieldInfo(Type type, HashSet<FieldInfo> allFieldInfos)
        {
            FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
            Dictionary<string, FieldInfo> dicField = new Dictionary<string, FieldInfo>();
            foreach (FieldInfo field in fields)
            {
                if (field.IsDefined(typeof(ClassPoolCheckIgnore), true))
                {
                    continue;
                }
                dicField.Add(field.Name, field);
            }
            //忽略打了标签的属性对应的backing字段
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
            foreach (PropertyInfo property in properties)
            {
                if (property.IsDefined(typeof(ClassPoolCheckIgnore), true))
                {
                    dicField.Remove($"<{property.Name}>k__BackingField");
                }
            }

            foreach (var item in dicField)
            {
                allFieldInfos.Add(item.Value);
            }

            foreach (Type baseType in GetBaseTypes(type))
            {
                if (baseType.IsClass)
                {
                    GetAllFieldInfo(baseType, allFieldInfos);
                }
            }
        }

        private static IEnumerable<Type> GetBaseTypes(this Type type, bool includeSelf = false)
        {
            IEnumerable<Type> enumerable = GetBaseClasses(type, includeSelf).Concat(type.GetInterfaces());
            if (includeSelf && type.IsInterface)
            {
                enumerable.Concat(new Type[1] { type });
            }

            return enumerable;
        }

        private static IEnumerable<Type> GetBaseClasses(this Type type, bool includeSelf = false)
        {
            if (!(type == null) && !(type.BaseType == null))
            {
                if (includeSelf)
                {
                    yield return type;
                }

                Type current = type.BaseType;
                while (current != null)
                {
                    yield return current;
                    current = current.BaseType;
                }
            }
        }

        private static void GetErrorField(object obj, object templateObj, string fieldNamePrefix, Dictionary<FieldInfo, string> errorFields, HashSet<object> checkedObjs)
        {
            if (checkedObjs.Contains(obj))
            {
                //防止循环引用, 无限递归
                return;
            }
            checkedObjs.Add(obj);

            Type type = obj.GetType();
            HashSet<FieldInfo> allFieldInfos;
            if (!_typeFieldInfoCache.TryGetValue(type, out allFieldInfos))
            {
                allFieldInfos = new HashSet<FieldInfo>();
                GetAllFieldInfo(type, allFieldInfos);
                _typeFieldInfoCache.Add(type, allFieldInfos);
            }
            foreach (FieldInfo field in allFieldInfos)
            {
                object value = field.GetValue(obj);
                var templateValue = field.GetValue(templateObj);
                if (value == null)
                {
                    if (templateValue != null)
                    {
                        errorFields.TryAdd(field, fieldNamePrefix + field.Name);
                        continue;
                    }
                    else
                    {
                        //自己的值和模板对象的值都为null
                        continue;
                    }
                }
                else if (value != null && templateValue == null)
                {
                    errorFields.TryAdd(field, fieldNamePrefix + field.Name);
                    continue;
                }
                else if (value is IConcurrentReusableClass || value is IReusableClass)
                {
                    GetErrorField(value, templateValue, fieldNamePrefix + field.Name + "->", errorFields, checkedObjs);
                    continue;
                }
                else if (value is string)
                {
                    if (!Equals(value, templateValue))
                    {
                        errorFields.TryAdd(field, fieldNamePrefix + field.Name);
                        continue;
                    }
                }
                else if (value is Array array)
                {
                    if (!AreArraysEqual(array, templateValue as Array))
                    {
                        errorFields.TryAdd(field, fieldNamePrefix + field.Name);
                        continue;
                    }
                }
                else if (value is IEnumerable enumerable)
                {
                    if (!AreIteratorsEqual(enumerable.GetEnumerator(), (templateValue as IEnumerable).GetEnumerator()))
                    {
                        errorFields.TryAdd(field, fieldNamePrefix + field.Name);
                        continue;
                    }
                }
                else if (value is Delegate delegate1)
                {
                    if (delegate1.Method != (templateValue as Delegate).Method)
                    {
                        errorFields.TryAdd(field, fieldNamePrefix + field.Name);
                        continue;
                    }
                }
                else
                {
                    if (value.GetType().Namespace.StartsWith("IQIGame"))//为了节约性能, 暂时只判断自己写的class
                    {
                        GetErrorField(value, templateValue, fieldNamePrefix + field.Name + "->", errorFields, checkedObjs);
                        continue;
                    }
                    else
                    {
                        if (!Equals(templateValue, value))
                        {
                            errorFields.TryAdd(field, fieldNamePrefix + field.Name);
                            continue;
                        }
                    }

                }
            }
        }

        private static bool AreArraysEqual(Array array1, Array array2)
        {
            if (array1.Rank != array2.Rank)
            {
                return false;
            }
            if (array1.Length != array2.Length)
            {
                return false;
            }

            for (int i = 0; i < array1.Length; i++)
            {
                object value1 = array1.GetValue(i);
                object value2 = array2.GetValue(i);

                if (value1 == null && value2 == null)
                {
                    continue;
                }
                if (value1 == null && value2 != null)
                {
                    return false;
                }
                if (value1 != null && value2 == null)
                {
                    return false;
                }
                if (value1.GetType().IsArray && value2.GetType().IsArray)
                {
                    if (!AreArraysEqual((Array)value1, (Array)value2))
                    {
                        return false;
                    }
                }
                if (!value1.Equals(value2))//这里是可以递归判断GetErrorField的, 暂时简单判断Equals就够了
                {
                    return false;
                }
            }
            return true;
        }

        private static bool AreIteratorsEqual(IEnumerator iterator1, IEnumerator iterator2)
        {
            while (iterator1.MoveNext())
            {
                if (!iterator2.MoveNext() || !Equals(iterator1.Current, iterator2.Current))//这里是可以递归判断GetErrorField的, 暂时简单判断Equals就够了
                {
                    return false;
                }
            }

            return !iterator2.MoveNext();
        }

        private static object GetTypeDefaultInstance(Type type)
        {
            object templateObj;
            if (!_typeTempleteObjCache.TryGetValue(type, out templateObj))
            {
                templateObj = Activator.CreateInstance(type);
                _typeTempleteObjCache.Add(type, templateObj);
            }

            return templateObj;
        }


        //private static bool ObjIsDefault(object obj)
        //{
        //    if (obj == null)
        //    {
        //        return true;
        //    }
        //    Type type = obj.GetType();
        //    if (type.IsValueType)
        //    {
        //        return Equals(obj, GetTypeDefaultInstance(type));
        //    }
        //    return false;
        //}
    }

    public class ClassPoolCheckIgnore : Attribute
    {
        public ClassPoolCheckIgnore()
        {
        }
    }
}
